//
//  GameScene.m
//  CandyCrushClone
//
//  Created by  Stefan Nestorov on 12/29/14.
//  Copyright (c) 2014 Nestorov Engineering. All rights reserved.
//

#import "RWTMyScene.h"
#import "RWTCookie.h"
#import "RWTLevel.h"
#import "RWTSwap.h"
#import "GameInfo.h"
#import "Globals.h"
#import "GameInfo.h"
#import "PlayerInterfaceSprite.h"
#import "SKTUtils.h"
#import "SKTEffects.h"
#import "WinConditionsSprite.h"
#import "CommunicationManager.h"
#import "AudioController.h"

#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
#define SYSTEM_VERSION_LESS_THAN(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

static const CGFloat TileWidth = 35.0;// 32.0;
static const CGFloat TileHeight = 36.0;

static const NSInteger MAX_MOVE_LENGTH = 21;

static SKAction *ThrowBombSound;
static SKAction *SwapSound;
static SKAction *InvalidSwapSound;
static SKAction *MatchSound;
static SKAction *FallingCookieSound;
static SKAction *AddCookieSound;
static SKAction *BombSound;

@interface RWTMyScene ()

@property (strong, nonatomic) SKNode *gameLayer;
@property (strong, nonatomic) SKNode *cookiesLayer;
@property (strong, nonatomic) SKNode *tilesLayer;

@property (assign, nonatomic) NSInteger swipeFromColumn;
@property (assign, nonatomic) NSInteger swipeFromRow;
@property (strong, nonatomic) SKSpriteNode *selectionSprite;
@property (strong, nonatomic) SKSpriteNode *menuScreen;
@property (strong, nonatomic) SKSpriteNode *yesButton;
@property (strong, nonatomic) SKSpriteNode *noButton;
@property (strong, nonatomic) SKSpriteNode *backToMenuButton;
@property (strong, nonatomic) SKSpriteNode *playNewGameButton;
@property (strong, nonatomic) NSMutableArray *cookiesTextures;
//@property (strong, nonatomic) NSMutableArray *rayAnimationTextures;

@end


@implementation RWTMyScene
{
    GameInfo *_gameInfo;
    BOOL _shouldShowHint;
    BOOL _isSmallDisplay;
    SKLabelNode *_countDownLabel;
    NSInteger _moveLength;
    SKNode *_hudLayer;
    SKEmitterNode *_followFingerParticle;
    NSInteger _followFingerParticleBirthRate;
    SKSpriteNode *_menuButton;
    SKSpriteNode *_timerPad;
    SKTextureAtlas *_cookiesAtlas;
//    SKTextureAtlas *_rayAnimationAtlas;
    
    PlayerInterfaceSprite *_localPlayerLayer;
    PlayerInterfaceSprite *_otherPlayerLayer;
    SKSpriteNode *_glassSpite;
    SKSpriteNode *_cookieBundleSprite;
    SKSpriteNode *_starSprite;
    
}


- (id)initWithSize:(CGSize)size
{
    
    if ((self = [super initWithSize:size])) {
        
        _gameInfo = [GameInfo sharedInfo];
        
        self.anchorPoint = CGPointMake(0, 0);
        
        // random number (1 ... 8)
        int backgroundNumber = arc4random() % 8 + 1;
        SKSpriteNode *background;
        
        // Cash background image for iOS 8 and later
        if ( SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0") ) {
            
            NSString *backgroundImageName = [NSString stringWithFormat:@"boardBackground_%d@2x",backgroundNumber];
            NSString *path = [[NSBundle mainBundle] pathForResource:backgroundImageName ofType:@"png"];
            UIImage *backgroundImage = [UIImage imageWithContentsOfFile:path];
            SKTexture *backgroundTexture = [SKTexture textureWithImage:backgroundImage];
            background = [[SKSpriteNode alloc] initWithTexture:backgroundTexture];
            
        } else {
            
            // do Not cash background image
            NSString *backgroundImageName = [NSString stringWithFormat:@"boardBackground_%d",backgroundNumber];
            background = [SKSpriteNode spriteNodeWithImageNamed:backgroundImageName];
            
        }
        
        background.anchorPoint = CGPointMake(0, 0);
        [self addChild:background];
        
        self.gameLayer = [SKNode node];
        self.gameLayer.hidden = YES;
        [self addChild:self.gameLayer];
        
        CGPoint layerPosition = CGPointMake(size.width / 2 -TileWidth*NumColumns/2, 10);
        
        self.tilesLayer = [SKNode node];
        self.tilesLayer.position = layerPosition;
        [self.gameLayer addChild:self.tilesLayer];
        
        self.cookiesLayer = [SKNode node];
        self.cookiesLayer.position = layerPosition;
        
        [self.gameLayer addChild:self.cookiesLayer];
        
        self.swipeFromColumn = self.swipeFromRow = NSNotFound;
        
        self.selectionSprite = [SKSpriteNode node];
        self.selectionSprite.anchorPoint = CGPointMake(0.5, 0);
        
        [self preloadResources];
        
        // Listen for OpponentSwap
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(performSwapFromServer:)  name:PERFORM_SWAP object:nil];
        
        [self initializeHUDLayer];
        [self initParticles];
        
        _gameState = GameStateStartingLevel;
        _canMakeMove = YES;
        _isSmallDisplay = NO;
        
    }
    
    return self;
    
}


-(void)initializeHUDLayer
{
    
    _hudLayer = [SKNode node];
    _hudLayer.zPosition = 100;
    [self addChild:_hudLayer];
    
    [self addLocalPlayerLayer];
    [self addOtherPlayerLayer];
    
    [self addExitButton];
    [self addTimer];
    [self addGlass];
    
}


- (void)initParticles
{
    
    _followFingerParticleBirthRate = 500;
    
    NSString *myParticlePath = [[NSBundle mainBundle] pathForResource:@"FollowFingerMagicParticle" ofType:@"sks"];
    _followFingerParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
    _followFingerParticle.particleBirthRate = 0;
    _followFingerParticle.zPosition = 100;
    
    [self addChild:_followFingerParticle];
    
}


- (void)addLocalPlayerLayer
{
    
    _localPlayerLayer = [[PlayerInterfaceSprite alloc] initForLocalPlayer:YES];
    CGPoint localPlayerLayerPositon = CGPointMake(_localPlayerLayer.size.width / 2 + 1, self.size.height - _localPlayerLayer.size.height / 2 - 8);
    _localPlayerLayer.position = localPlayerLayerPositon;
    
    [_hudLayer addChild:_localPlayerLayer];
    
}


- (void)addOtherPlayerLayer
{
    
    // add OtherPlayerLayer
    _otherPlayerLayer = [[PlayerInterfaceSprite alloc] initForLocalPlayer:NO];
    CGPoint otherPlayerLayerPositon = CGPointMake( self.size.width - _otherPlayerLayer.size.width / 2, self.size.height - _otherPlayerLayer.size.height / 2 - 8);
    _otherPlayerLayer.position = otherPlayerLayerPositon;
    
    [_hudLayer addChild:_otherPlayerLayer];
    
}


- (void)addTimer
{
    
    _timerPad = [SKSpriteNode spriteNodeWithImageNamed:@"timerPadGreen"];
    _timerPad.position = CGPointMake(self.size.width / 2, 365);
    
    [_hudLayer addChild:_timerPad];
    
    // Add timer label
    _countDownLabel = [SKLabelNode labelNodeWithFontNamed:@"MarkerFelt-Wide"];
    [_countDownLabel setFontSize:28];
    [_countDownLabel setFontColor:[UIColor colorWithRed:32.0/255.0 green:191.0/255.0 blue:32.0/255.0 alpha:1]];
    _countDownLabel.text = [NSString stringWithFormat:@"%zd", MAX_MOVE_LENGTH];
    _countDownLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter;
    [_countDownLabel setPosition:CGPointMake(self.size.width / 2 - 1, 355.5)];
    [_hudLayer addChild:_countDownLabel];
   
}


- (void)addGlass
{
    
    SKSpriteNode *glassSprite = [SKSpriteNode spriteNodeWithImageNamed:@"glass"];
    glassSprite.name = @"glassSprite";
    glassSprite.anchorPoint = CGPointMake(0, 0);
    [_hudLayer addChild:glassSprite];
    
}


- (void)addExitButton
{
    
    _menuButton = [SKSpriteNode spriteNodeWithImageNamed:@"exitButton"];
    _menuButton.position = CGPointMake(self.size.width / 2, 450);
    [_hudLayer addChild:_menuButton];
    
}


- (void)preloadResources
{
    
    InvalidSwapSound = [SKAction playSoundFileNamed:@"InvalidSwapSound.wav" waitForCompletion:NO];
    AddCookieSound = [SKAction playSoundFileNamed:@"Drip.wav" waitForCompletion:NO];
    FallingCookieSound = [SKAction playSoundFileNamed:@"Drip.wav" waitForCompletion:NO];
    BombSound= [SKAction playSoundFileNamed:@"BombSound.wav" waitForCompletion:NO];
    MatchSound = [SKAction playSoundFileNamed:@"CollectCookieSound.wav" waitForCompletion:NO];
    SwapSound = [SKAction playSoundFileNamed:@"SwapCookieSound.wav" waitForCompletion:NO];
    ThrowBombSound = [SKAction playSoundFileNamed:@"ThrowBombSound.wav" waitForCompletion:NO];
    
    [SKLabelNode labelNodeWithFontNamed:@"GillSans-BoldItalic"];
    
    // Uncomment to preload RayAnimation
//    _rayAnimationAtlas = [SKTextureAtlas atlasNamed:@"rayAnimation"];
//    _rayAnimationTextures = [NSMutableArray new];
//    
//    for ( int i = 1; i <= 20; i++ ) {
//        SKTexture *texture = [_rayAnimationAtlas textureNamed:[NSString stringWithFormat:@"ray_%d",i]];
//        [_rayAnimationTextures addObject:texture];
//    }
//    
//    // Preloads _rayAnimationTextures
//    SKSpriteNode *someSprite = [SKSpriteNode spriteNodeWithTexture:_rayAnimationTextures[0]];
//    someSprite.alpha = 1;
    
    // Preload cookies set needed for FireworksAnimation
    _cookiesAtlas = [SKTextureAtlas atlasNamed:@"Set1"];
    _cookiesTextures = [NSMutableArray new];
    
    for ( int i = 1; i <= 6; i++ ) {
        SKTexture *texture = [_cookiesAtlas textureNamed:[NSString stringWithFormat:@"%dset1",i]];
        [_cookiesTextures addObject:texture];
    }
    
    // Preloads _rayAnimationTextures
    SKSpriteNode *someSprite = [SKSpriteNode spriteNodeWithTexture:_cookiesTextures[0]];
    someSprite.alpha = 1;
    
    // Preload game over sprites
    _glassSpite = [SKSpriteNode spriteNodeWithImageNamed:@"glass"];
    _cookieBundleSprite = [SKSpriteNode spriteNodeWithImageNamed:@"cookieBundleImage"];
    _starSprite = [SKSpriteNode spriteNodeWithImageNamed:@"star"];
    _backToMenuButton = [SKSpriteNode spriteNodeWithImageNamed:@"homeButton"];
    _playNewGameButton = [SKSpriteNode spriteNodeWithImageNamed:@"playButtonSmall"];
    
}

- (void)addSpritesForCookies:(NSSet *)cookies
{
    
    // Moves all Cookies from center of the board to their positions
    CGPoint centerPosition = CGPointMake(self.size.width / 2 - TileWidth/2 , TileWidth*NumColumns/2 + TileHeight / 2);
    
    for (RWTCookie *cookie in cookies) {
        SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:[cookie spriteName]];
        sprite.anchorPoint = CGPointMake(0.5, 0);
        sprite.position = centerPosition;
        
        CGPoint targetPosition = [self pointForColumn:cookie.column row:cookie.row];
        
        double randWait = drand48() * 0.3;
        SKAction *waitBeforeMove = [SKAction waitForDuration:randWait];
        
        SKAction *moveToTargetPosition = [SKAction moveTo:targetPosition duration:0.3];
        SKAction *sequence = [SKAction sequence:@[waitBeforeMove, moveToTargetPosition]];
        
        [self.cookiesLayer addChild:sprite];
        cookie.sprite = sprite;
        
        [cookie.sprite runAction:sequence];
        
    }
    
}


- (void)addSpritesForCookiesFromServer:(NSSet *)cookies
{
    
    // remove all cookie sprites
    for (SKSpriteNode *cookieSprite in self.cookiesLayer.children) {
        [cookieSprite removeFromParent];
    }
    
    // add new cookie sprites
    for (RWTCookie *cookie in cookies) {
        
        SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:[cookie spriteName]];
        sprite.anchorPoint = CGPointMake(0.5, 0);
        sprite.position = [self pointForColumn:cookie.column row:cookie.row];
        cookie.sprite = sprite;
        
        [self.cookiesLayer addChild:sprite];
        
    }
    
}


- (void)addTiles
{
    
    for (NSInteger row = 0; row < NumRows; row++) {
        for (NSInteger column = 0; column < NumColumns; column++) {
            
            if ([self.level tileAtColumn:column row:row] != nil) {
                SKSpriteNode *tileNode = [SKSpriteNode spriteNodeWithImageNamed:@"Tile"];
                tileNode.position = [self tilePointForColumn:column row:row];
                [self.tilesLayer addChild:tileNode];
            }
            
        }
    }
    
}


- (CGPoint)tilePointForColumn:(NSInteger)column row:(NSInteger)row
{
    
    return CGPointMake(column*TileWidth + TileWidth/2, row*TileHeight + TileHeight/2);
    
}

- (CGPoint)pointForColumn:(NSInteger)column row:(NSInteger)row
{

    return CGPointMake(column*TileWidth + TileWidth/2, row*TileHeight);
    
}

- (BOOL)convertPoint:(CGPoint)point toColumn:(NSInteger *)column row:(NSInteger *)row
{
    
    NSParameterAssert(column);
    NSParameterAssert(row);
    
    // Is this a valid location within the cookies layer? If yes,
    // calculate the corresponding row and column numbers.
    if (point.x >= 0 && point.x < NumColumns*TileWidth &&
        point.y >= 0 && point.y < NumRows*TileHeight) {
        
        *column = point.x / TileWidth;
        *row = point.y / TileHeight;
        
        return YES;
        
    } else {
        *column = NSNotFound;  // invalid location
        *row = NSNotFound;
        return NO;
    }
    
}


- (void)adjustSceneForSmallDisplay
{
    
    _isSmallDisplay = YES;
    
    // Scale down player layers
    [_localPlayerLayer setScale:0.77f];
    [_otherPlayerLayer setScale:0.77f];
    
    // Move player layers down
    CGPoint localPlayerLayerPosition = _localPlayerLayer.position;
    localPlayerLayerPosition.x -= 10;
    localPlayerLayerPosition.y -= 48;
    _localPlayerLayer.position = localPlayerLayerPosition;
    
    CGPoint otherPlayerLayerPosition = _otherPlayerLayer.position;
    otherPlayerLayerPosition.x += 10;
    otherPlayerLayerPosition.y -= 48;
    _otherPlayerLayer.position = otherPlayerLayerPosition;
    
    // Move down timer label, pad, menuButton
    CGPoint countDownLabelPosition = _countDownLabel.position;
    countDownLabelPosition.y += 40;
    _countDownLabel.position = countDownLabelPosition;
    
    CGPoint timerPadPosition = _timerPad.position;
    timerPadPosition.y += 40;
    _timerPad.position = timerPadPosition;
    
    CGPoint menuButtonPosition = _menuButton.position;
    menuButtonPosition.x += 1;
    menuButtonPosition.y += 5;
    _menuButton.position = menuButtonPosition;
    
}


#pragma mark - Touch events

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    
    UITouch *touch = [touches anyObject];
    CGPoint cookiesLayerLocation = [touch locationInNode:self.cookiesLayer];
    
    CGPoint hudLocation = [touch locationInNode:_hudLayer];
    CGPoint menuScreenLocation = [touch locationInNode:_menuScreen];
    
    SKNode *buttonsPad = [_hudLayer childNodeWithName:@"butonsPad"];
    CGPoint gameOverButtonsPadLocation = [touch locationInNode:buttonsPad];
    
    // show Particle
    _followFingerParticle.particlePosition = CGPointAdd(hudLocation, CGPointMake(0, 10));
    _followFingerParticle.particleBirthRate = _followFingerParticleBirthRate;
    
    switch ( _gameState ) {
        
        case GameStateStartingLevel:
            
            return;
            
        case GameStateInMenu:
            
            if ( _noButton && [_noButton containsPoint:menuScreenLocation] ) {
                
                [self didPressNoButton];
                
            } else if ( _yesButton && [_yesButton containsPoint:menuScreenLocation] ) {
                
                [self didPressYesButton];
                
            }
            
            return;
            
        case GameStateGameOver:
            
            if ( [_playNewGameButton containsPoint:gameOverButtonsPadLocation] ) {
                
                [self didPressStartNewGameButton];
                
            } else if ( [_backToMenuButton containsPoint:gameOverButtonsPadLocation] ) {
                
                [self didPressYesButton];
                
            }
            
            return;
            
        case GameStatePlaying:
            
            if ( [_menuButton containsPoint:hudLocation] ) {
                
                [self didPressExitButton];
                
            } else if ( [_localPlayerLayer containsPoint:hudLocation] ) {
                
                [_localPlayerLayer changeView];
                
            } else if ( [_otherPlayerLayer containsPoint:hudLocation] ) {
                
                [_otherPlayerLayer changeView];
                
            }
            
            break;
            
        default:
            return;
    }
    
    if ( _canMakeMove == NO ) return;
    
    NSInteger column, row;
    if ( [self convertPoint:cookiesLayerLocation toColumn:&column row:&row] ) {
        
        // do NOT make move if, is NOT my turn or if Game is Over
        if ( [GameInfo sharedInfo].isMyTurn == NO ) return;
        if ( [GameInfo sharedInfo].isGameOver == YES ) return;
        
        RWTCookie *cookie = [self.level cookieAtColumn:column row:row];
        
        if (cookie != nil) {
            
            [self showSelectionIndicatorForCookie:cookie];
            self.swipeFromColumn = column;
            self.swipeFromRow = row;
        }
        
    }
    
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    
    UITouch *touch = [touches anyObject];
    CGPoint cookiesLayerLocation = [touch locationInNode:self.cookiesLayer];
    
    CGPoint hudLocation = [touch locationInNode:_hudLayer];
    _followFingerParticle.particlePosition = CGPointAdd(hudLocation, CGPointMake(0, 10));
    
    if (self.swipeFromColumn == NSNotFound) return;
    
    // do NOT make move if, is NOT my turn or if Game is Over
    if ( [GameInfo sharedInfo].isMyTurn == NO ) return;
    if ( [GameInfo sharedInfo].isGameOver == YES ) return;
    
    NSInteger column, row;
    if ( [self convertPoint:cookiesLayerLocation toColumn:&column row:&row] ) {
        
        NSInteger horzDelta = 0, vertDelta = 0;
        if (column < self.swipeFromColumn) {          // swipe left
            horzDelta = -1;
        } else if (column > self.swipeFromColumn) {   // swipe right
            horzDelta = 1;
        } else if (row < self.swipeFromRow) {         // swipe down
            vertDelta = -1;
        } else if (row > self.swipeFromRow) {         // swipe up
            vertDelta = 1;
        }
        
        if (horzDelta != 0 || vertDelta != 0) {
            [self trySwapHorizontal:horzDelta vertical:vertDelta];
            [self hideSelectionIndicator];
            self.swipeFromColumn = NSNotFound;
        }
    }
    
}


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
 
    // Hide particle
    _followFingerParticle.particleBirthRate = 0;
    
    if (self.selectionSprite.parent != nil && self.swipeFromColumn != NSNotFound) {
        [self hideSelectionIndicator];
    }
    self.swipeFromColumn = self.swipeFromRow = NSNotFound;
    
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    
    [self touchesEnded:touches withEvent:event];
    
}


- (void)didPressExitButton
{
    
    [[AudioController sharedInstance] playButtonClick];
    
    _gameState = GameStateInMenu;
    
    // Init menu screen
    _menuScreen = [SKSpriteNode spriteNodeWithImageNamed:@"exitMenu"];
    _menuScreen.position = CGPointMake(self.size.width / 2, self.size.height / 2);
    _menuScreen.zPosition = 101;
    [_menuScreen setScale:0];
    [_hudLayer addChild:_menuScreen];
    
    // add abortButton
    _yesButton = [SKSpriteNode spriteNodeWithImageNamed:@"exitYesButton"];
    _yesButton.position = CGPointMake(-60, -30);
    [_menuScreen addChild:_yesButton];
    
    // add infoButton
    _noButton = [SKSpriteNode spriteNodeWithImageNamed:@"exitNoButton"];
    _noButton.position = CGPointMake(60, -30);
    [_menuScreen addChild:_noButton];
    
    SKAction *scaleToMax = [SKAction scaleTo:1 duration:0.25];
    SKAction *scaleBounce =  [SKAction scaleTo:0.9 duration:0.1];
    SKAction *scaleToNormal = [SKAction scaleTo:1 duration:0.1];
    SKAction *sequence = [SKAction sequence:@[scaleToMax, scaleBounce, scaleToNormal]];
    
    [_menuScreen runAction:sequence];
    
}


- (void)didPressYesButton
{
    
    [[AudioController sharedInstance] playButtonClick];
    [CommunicationManager sharedManager].isInGame = NO;
    [self cleanScene];
    [self.sceneDelegate abortGame];
    
}


- (void)didPressNoButton
{
    
    [[AudioController sharedInstance] playButtonClick];
    
    // Close menu
    SKAction *scaleToZero = [SKAction scaleTo:0 duration:0.25];
    scaleToZero.timingMode = SKActionTimingEaseIn;
    
    __weak RWTMyScene *weakSelf = self;
    
    [_menuScreen runAction:scaleToZero completion:^{
        
        [weakSelf.noButton removeFromParent];
        [weakSelf.yesButton removeFromParent];
        [weakSelf.menuScreen removeFromParent];
        weakSelf.gameState = GameStatePlaying;
        
    }];
    
}


- (void)didPressStartNewGameButton
{
    
    [[AudioController sharedInstance] playButtonClick];
    
    BOOL hasEnoughEnergy = [self hasEnoughEnergy];
    
    if ( hasEnoughEnergy == YES ) {
    
        [self.sceneDelegate startNewGame];
        
    } else {
        
        // Exit to MainMenuVC
        [self didPressYesButton];
        
    }
    
}


- (BOOL)hasEnoughEnergy
{
    
    NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
    
    NSInteger currentEnergy = [settings integerForKey:CURRENT_ENERGY_KEY];
    BOOL isPurchasedUnlimitedEnergy = [settings boolForKey:IS_PURCHASED_UNLIMITED_ENERGY_KEY];
    
    if ( isPurchasedUnlimitedEnergy == YES || currentEnergy >= 1 ) {
        
        return YES;
        
    }
    
    return NO;
    
}


- (void)trySwapHorizontal:(NSInteger)horzDelta vertical:(NSInteger)vertDelta
{
    
    NSInteger toColumn = self.swipeFromColumn + horzDelta;
    NSInteger toRow = self.swipeFromRow + vertDelta;
    
    if (toColumn < 0 || toColumn >= NumColumns) return;
    if (toRow < 0 || toRow >= NumRows) return;
    
    RWTCookie *toCookie = [self.level cookieAtColumn:toColumn row:toRow];
    if (toCookie == nil) return;
    

    RWTCookie *fromCookie = [self.level cookieAtColumn:self.swipeFromColumn row:self.swipeFromRow];
    
    if (self.swipeHandler != nil) {
        RWTSwap *swap = [[RWTSwap alloc] init];
        swap.cookieA = fromCookie;
        swap.cookieB = toCookie;
        
        self.swipeHandler(swap, NO);
    }
    
}

#pragma mark - Animate matched cookies

- (void)animateMatchedCookies:(NSSet *)chains completion:(dispatch_block_t)completion
{
    
    for (RWTChain *chain in chains) {
        
        for (RWTCookie *cookie in chain.cookies) {
            
            if (cookie.sprite != nil) {
                
                SKSpriteNode *targetCookie = [self getTargetCookieForCookie:cookie];
                [self playCurveAnimationForCookie:cookie.sprite toTargetCookie:targetCookie minAngle:30 maxAngle:150 speed:350 radius:150 isHeroSkill:NO particleColor:[self getColorForCookieType:cookie.cookieType]];
                
                cookie.sprite = nil;
            }
        }
        
    }
    
    [self playComboAnimationIfNecessary];
    
    [self runAction:MatchSound];
    
    [self runAction:[SKAction sequence:@[
                                         [SKAction waitForDuration:0.3],
                                         [SKAction runBlock:completion]
                                         ]]];
    
}

#pragma mark - Animate Swaps

- (void)animateSwap:(RWTSwap *)swap completion:(dispatch_block_t)completion
{
    
    // Put the cookie you started with on top.
    swap.cookieA.sprite.zPosition = 100;
    swap.cookieB.sprite.zPosition = 90;
    
    const NSTimeInterval Duration = 0.3;
    
    SKAction *moveA = [SKAction moveTo:swap.cookieB.sprite.position duration:Duration];
    moveA.timingMode = SKActionTimingEaseOut;
    [swap.cookieA.sprite runAction:[SKAction sequence:@[moveA, [SKAction runBlock:completion]]]];
    
    SKAction *moveB = [SKAction moveTo:swap.cookieA.sprite.position duration:Duration];
    moveB.timingMode = SKActionTimingEaseOut;
    [swap.cookieB.sprite runAction:moveB];
    
    [self runAction:SwapSound];
    
}

- (void)animateInvalidSwap:(RWTSwap *)swap completion:(dispatch_block_t)completion
{
    
    swap.cookieA.sprite.zPosition = 100;
    swap.cookieB.sprite.zPosition = 90;
    
    const NSTimeInterval Duration = 0.2;
    
    SKAction *moveA = [SKAction moveTo:swap.cookieB.sprite.position duration:Duration];
    moveA.timingMode = SKActionTimingEaseOut;
    
    SKAction *moveB = [SKAction moveTo:swap.cookieA.sprite.position duration:Duration];
    moveB.timingMode = SKActionTimingEaseOut;
    
    [swap.cookieA.sprite runAction:[SKAction sequence:@[moveA, moveB, [SKAction runBlock:completion]]]];
    [swap.cookieB.sprite runAction:[SKAction sequence:@[moveB, moveA]]];
    
    [self runAction:InvalidSwapSound];
    
}

#pragma mark - Show/Hide selected sprite

- (void)showSelectionIndicatorForCookie:(RWTCookie *)cookie
{
    
    // If the selection indicator is still visible, then first remove it.
    if (self.selectionSprite.parent != nil) {
        [self.selectionSprite removeFromParent];
    }
    
    SKTexture *texture = [SKTexture textureWithImageNamed:[cookie highlightedSpriteName]];
    self.selectionSprite.size = texture.size;
    [self.selectionSprite runAction:[SKAction setTexture:texture]];
    
    [cookie.sprite addChild:self.selectionSprite];
    self.selectionSprite.alpha = 1.0;
    
}

- (void)hideSelectionIndicator
{
    
    [self.selectionSprite runAction:[SKAction sequence:@[
                                                         [SKAction fadeOutWithDuration:0.3],
                                                         [SKAction removeFromParent]]]];
    
}

#pragma mark - Animate falling cookies

- (void)animateFallingCookies:(NSArray *)columns completion:(dispatch_block_t)completion
{

    __block NSTimeInterval longestDuration = 0;
    
    for (NSArray *array in columns) {
        [array enumerateObjectsUsingBlock:^(RWTCookie *cookie, NSUInteger idx, BOOL *stop) {
            
            if ( [cookie.sprite actionForKey:@"squash"] != nil ) {
                
                // stop Squash animations
                [cookie.sprite removeActionForKey:@"squash"];
                [cookie.sprite setScale:1];
                
            }
            
            CGPoint newPosition = [self pointForColumn:cookie.column row:cookie.row];
            
            NSTimeInterval delay = 0.05 + 0.15*idx;
            
            NSTimeInterval duration = ((cookie.sprite.position.y - newPosition.y) / TileHeight) * 0.115;
            
            if ( duration == 0.115 ) {
                duration = 0.18;
            }
            
            longestDuration = MAX(longestDuration, duration + delay);
            
            SKAction *moveAction = [SKAction moveTo:newPosition duration:duration];
            
            SKTScaleEffect *fallingScaleEffect = [self createHorizontalScaleEffectForSprite:cookie.sprite scaleFactor:1.2 withDuration:duration andTimingFunction:SKTTimingFunctionElasticEaseIn];
            SKTScaleEffect *squashScaleEffect = [self createVerticalScaleEffectForSprite:cookie.sprite scaleFactor:1.45f withDuration:1.2f andTimingFunction:SKTTimingFunctionElasticEaseOut];
            
            SKAction *squashSequence = [SKAction sequence:@[[SKAction actionWithEffect:fallingScaleEffect], [SKAction actionWithEffect:squashScaleEffect]]];
            
            [cookie.sprite runAction:[SKAction sequence:@[
                                                          [SKAction waitForDuration:delay],
                                                          [SKAction group:@[moveAction, squashSequence, FallingCookieSound]]]]
                             withKey:@"squash"];
            
        }];
    }
    
    [self runAction:[SKAction sequence:@[
                                         [SKAction waitForDuration:longestDuration],
                                         [SKAction runBlock:completion]
                                         ]]];
    
}


- (void)animateNewCookies:(NSArray *)columns completion:(dispatch_block_t)completion
{
   
    __block NSTimeInterval longestDuration = 0;
    
    for (NSArray *array in columns) {
        
        NSInteger startRow = ((RWTCookie *)[array firstObject]).row + 1;
        
        [array enumerateObjectsUsingBlock:^(RWTCookie *cookie, NSUInteger idx, BOOL *stop) {
            
            SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:[cookie spriteName]];
            sprite.anchorPoint = CGPointMake(0.5, 0);
            sprite.position = [self pointForColumn:cookie.column row:startRow];
            [self.cookiesLayer addChild:sprite];
            cookie.sprite = sprite;
            
            NSTimeInterval delay = 0.1 + 0.2*([array count] - idx - 1);
            
            NSTimeInterval duration = (startRow - cookie.row) * 0.115;
            
            if ( duration == 0.115 ) {
                duration = 0.18;
            }
            
            longestDuration = MAX(longestDuration, duration + delay);
            
            CGPoint newPosition = [self pointForColumn:cookie.column row:cookie.row];
            SKAction *moveAction = [SKAction moveTo:newPosition duration:duration];
            
            SKTScaleEffect *fallingScaleEffect = [self createHorizontalScaleEffectForSprite:cookie.sprite scaleFactor:1.2 withDuration:duration andTimingFunction:SKTTimingFunctionElasticEaseIn];
            SKTScaleEffect *squashScaleEffect = [self createVerticalScaleEffectForSprite:cookie.sprite scaleFactor:1.5f withDuration:1.2f andTimingFunction:SKTTimingFunctionElasticEaseOut];
            
            SKAction *squashSequence = [SKAction sequence:@[[SKAction actionWithEffect:fallingScaleEffect], [SKAction actionWithEffect:squashScaleEffect]]];
            
            cookie.sprite.alpha = 0;
            [cookie.sprite runAction:[SKAction sequence:@[
                                                          [SKAction waitForDuration:delay],
                                                          [SKAction group:@[
                                                                            [SKAction fadeInWithDuration:0.05], moveAction, squashSequence,  AddCookieSound]]]]
                             withKey:@"squash"];
            
        }];
    }
    
    [self runAction:[SKAction sequence:@[
                                         [SKAction waitForDuration:longestDuration],
                                         [SKAction runBlock:completion]
                                         ]]];
    
}


- (SKTScaleEffect *)createHorizontalScaleEffectForSprite:(SKSpriteNode *)sprite scaleFactor:(CGFloat)scaleFactor withDuration:(NSTimeInterval)duration andTimingFunction:(SKTTimingFunction)timingFunction
{
    
    CGPoint oldScale = CGPointMake(sprite.xScale, sprite.yScale);
    CGPoint newScale = oldScale;
    
    // Squash from Left and Rignt
    newScale.x /= scaleFactor;
    newScale.y *= scaleFactor;
    
    SKTScaleEffect *scaleEffect = [SKTScaleEffect effectWithNode:sprite
                                                        duration:duration
                                                      startScale:newScale
                                                        endScale:oldScale];
    
    scaleEffect.timingFunction = timingFunction; // SKTTimingFunctionElasticEaseIn;
    
    return scaleEffect;
    
}


- (SKTScaleEffect *)createVerticalScaleEffectForSprite:(SKSpriteNode *)sprite  scaleFactor:(CGFloat)scaleFactor withDuration:(NSTimeInterval)duration andTimingFunction:(SKTTimingFunction)timingFunction
{
    
    CGPoint squashOldScale = CGPointMake(sprite.xScale, sprite.yScale);
    CGPoint squashNewScale = squashOldScale;
    
    // Squash from Top and Bottom
    squashNewScale.x *= scaleFactor;
    squashNewScale.y /= scaleFactor;
    
    SKTScaleEffect *squashScaleEffect = [SKTScaleEffect effectWithNode:sprite duration:duration
                                                            startScale:squashNewScale
                                                              endScale:squashOldScale];
    
    squashScaleEffect.timingFunction = timingFunction;
    
    return squashScaleEffect;
    
}


- (SKTScaleEffect *)createZoomScaleEffectForSprite:(SKSpriteNode *)sprite scaleFactor:(CGFloat)scaleFactor withDuration:(NSTimeInterval)duration
{
    
    CGPoint oldScale = CGPointMake(sprite.xScale, sprite.yScale);
    CGPoint newScale = oldScale;
    
    // Zoom In
    newScale.x *= scaleFactor;
    newScale.y *= scaleFactor;
    
    SKTScaleEffect *scaleEffect = [SKTScaleEffect effectWithNode:sprite
                                                        duration:duration
                                                      startScale:newScale
                                                        endScale:oldScale];
    
    scaleEffect.timingFunction = SKTTimingFunctionElasticEaseInOut;
    
    return scaleEffect;
    
}

#pragma mark - Animate Begin/Game over

- (void)animateGameOver
{

    _gameState = GameStateGameOver;
    
    // Determines when localPlayer wins
    if ( _gameInfo.winnerID == _gameInfo.localPlayer.playerId ) {
        
        [self playWinAnimation];
        
    } else {
        
        [self playLoseAnimation];
        
    }
    
}


- (void)playWinAnimation
{
    
    __weak RWTMyScene *weakSelf = self;
    
    CGFloat moveDuration = 0.75f;
    [[AudioController sharedInstance] playEffect:kWinSound];
    
    [weakSelf showWinBackgroundForDuration:moveDuration];
    [weakSelf moveHero:_localPlayerLayer.heroSprite toCenterOfScreenForDuration:moveDuration];
    [weakSelf playSpotlightAnimationForSprite:_localPlayerLayer.heroSprite withScale:2.25 isForever:YES];
    [weakSelf showCookieBundleImageForDuration:moveDuration];
    [weakSelf showYouWinSpritesAfterDuration:moveDuration];
    [weakSelf playWinRepeatActionsAfterDuration:moveDuration * 3];
    [weakSelf showButtonsOnScreenAfterDuration:2];
    [weakSelf playFlashingStarsAnimation];
    
}


- (void)showWinBackgroundForDuration:(CGFloat)duration
{
    
    _glassSpite.position = CGPointMake(self.size.width/2, self.size.height/2);
    _glassSpite.alpha = 0;
    _glassSpite.zPosition = _localPlayerLayer.heroSprite.zPosition -1;
    [_hudLayer addChild:_glassSpite];
    
    SKAction *fadeIn = [SKAction fadeAlphaTo:1 duration:duration];
    [_glassSpite runAction:fadeIn];
    
    // Hide these sprites behind the backGroundSprite
    _localPlayerLayer.zPosition = _glassSpite.zPosition -1;
    _otherPlayerLayer.zPosition = _glassSpite.zPosition -1;
    _menuButton.zPosition = _glassSpite.zPosition -1;
    _timerPad.zPosition = _glassSpite.zPosition -1;
    _countDownLabel.zPosition = _glassSpite.zPosition -1;
    
}


- (void)moveHero:(SKSpriteNode *)heroSprite toCenterOfScreenForDuration:(CGFloat)moveDuration
{
    
    // Convert points to get accurate Hero position inside HudLayer
    CGPoint heroPositionInScene = [self convertPoint:heroSprite.position fromNode:heroSprite.parent];
    CGPoint heroPositionInHudLayer = [self convertPoint:heroPositionInScene toNode:_hudLayer]; // the same as heroPositionInScene
    
    [heroSprite removeFromParent];
    [_hudLayer addChild:heroSprite];
    heroSprite.position = heroPositionInHudLayer;
    
    CGPoint targetPosition;
    
    // Determine who is the winner
    if ( _gameInfo.winnerID == _gameInfo.localPlayer.playerId ) {
        targetPosition = CGPointMake(self.size.width/2, self.size.height/2 + 60);
    } else {
        targetPosition = CGPointMake(self.size.width/2, self.size.height/2 + 5);
    }
    
    if ( _isSmallDisplay == YES ) {
        targetPosition.y -= 30;
    }
    
    SKAction *moveTo = [SKAction moveTo:targetPosition duration:moveDuration];
    SKAction *scaleUp = [SKAction scaleTo:1 duration:moveDuration];
    scaleUp.timingMode = SKActionTimingEaseOut;
    SKAction *groupAction = [SKAction group:@[moveTo, scaleUp]];
    
    [heroSprite runAction:groupAction];
    
}


- (void)showYouWinSpritesAfterDuration:(CGFloat)duration
{
    
    NSInteger youWinSpritesYPosition = 500;
    
    if ( _isSmallDisplay == YES ) {
        youWinSpritesYPosition -= 60;
    }
    
    // Create two sprites
    SKSpriteNode *youSprite = [SKSpriteNode spriteNodeWithImageNamed:@"youImage"];
    youSprite.name = @"youSprite";
    youSprite.anchorPoint = CGPointMake(1, 0.5);
    youSprite.position = CGPointMake(-youSprite.size.width, youWinSpritesYPosition);
    [_hudLayer addChild:youSprite];
    
    SKSpriteNode *winSprite = [SKSpriteNode spriteNodeWithImageNamed:@"winImage"];
    winSprite.name = @"winSprite";
    winSprite.anchorPoint = CGPointMake(0, 0.5);
    winSprite.position = CGPointMake(self.size.width + winSprite.size.width, youWinSpritesYPosition);
    [_hudLayer addChild:winSprite];
    
    NSTimeInterval moveDuration = 0.25;
    
    CGPoint targetPosition = CGPointMake(self.size.width/2, 0);
    
    // Play youSprite animation
    SKAction *youSpriteAnimation = [self createWinSquashAnimationForSprite:youSprite toPosition:targetPosition waitDuration:duration moveDuration:moveDuration];
    [youSprite runAction:youSpriteAnimation];
    
    // Play winSprite animation
    SKAction *winSpriteAnimation = [self createWinSquashAnimationForSprite:winSprite toPosition:targetPosition waitDuration:duration moveDuration:moveDuration];
    [winSprite runAction:winSpriteAnimation];
    
    __weak RWTMyScene *weakSelf = self;
    __weak AudioController *weakAudioController = [AudioController sharedInstance];
    
    SKAction *coinsFireworksBlock = [SKAction runBlock:^{
        
        [weakSelf playStarsFireworksAnimationAtPosition:CGPointMake(weakSelf.size.width/2, youWinSpritesYPosition)];
        [weakAudioController playEffect:kCoinsExplosionSound];
        
    }];
    
    SKAction *waitBeforeFireworks = [SKAction waitForDuration:moveDuration + 0.85];
    
    [_hudLayer runAction:[SKAction sequence:@[waitBeforeFireworks, coinsFireworksBlock]]];
    
}


- (SKAction *)createWinSquashAnimationForSprite:(SKSpriteNode *)sprite toPosition:(CGPoint)targetPosition waitDuration:(NSTimeInterval)waitDuration moveDuration:(NSTimeInterval)moveDuration
{
    
    SKAction *moveToCenter = [SKAction moveToX:targetPosition.x duration:moveDuration];
    moveToCenter.timingMode = SKActionTimingEaseIn;
    
    SKAction *wait = [SKAction waitForDuration:waitDuration];
    
    SKTScaleEffect *verticalScaleEffect = [self createVerticalScaleEffectForSprite:sprite
                                                                       scaleFactor:1.25f
                                                                      withDuration:moveDuration
                                                                 andTimingFunction:SKTTimingFunctionLinear];
    SKAction *verticalScaleAction = [SKAction actionWithEffect:verticalScaleEffect];
    
    SKTScaleEffect *horizontalScaleEffect = [self createHorizontalScaleEffectForSprite:sprite
                                                                           scaleFactor:0.35f
                                                                          withDuration:1.25f
                                                                     andTimingFunction:SKTTimingFunctionElasticEaseOut];
    SKAction *horizontalScaleAction = [SKAction actionWithEffect:horizontalScaleEffect];
    
    SKAction *moveGroup = [SKAction group:@[moveToCenter, verticalScaleAction]];
    SKAction *sequence = [SKAction sequence:@[wait, moveGroup, horizontalScaleAction]];
    
    return sequence;
    
}


- (void)showCookieBundleImageForDuration:(CGFloat)moveDuration
{
    
    SKSpriteNode *cookieBundleSprite = [SKSpriteNode spriteNodeWithImageNamed:@"cookieBundleImage"];
    cookieBundleSprite.name = @"cookieBundle";
    cookieBundleSprite.position = CGPointMake(self.size.width / 2, -cookieBundleSprite.size.height/2);
    [_hudLayer addChild:cookieBundleSprite];
    
    NSInteger bundleYPosition = self.size.height/2;
    if ( _isSmallDisplay == YES ) {
        bundleYPosition -= 30;
    }
    
    SKAction *moveToCenter = [SKAction moveToY:bundleYPosition duration:moveDuration];
    moveToCenter.timingMode = SKActionTimingEaseOut;
    
    [cookieBundleSprite runAction:moveToCenter];
    
}


- (void)playBounceCookiesBundleAnimationWithScaleFactor:(CGFloat)scaleFactor
{
    
    SKSpriteNode *cookieBundleSprite = (SKSpriteNode *)[_hudLayer childNodeWithName:@"cookieBundle"];
    
    if ( cookieBundleSprite == nil ) return;
    
    SKTScaleEffect *verticalScaleEffect = [self createVerticalScaleEffectForSprite:cookieBundleSprite
                                                                       scaleFactor:scaleFactor
                                                                      withDuration:2.5
                                                                 andTimingFunction:SKTTimingFunctionElasticEaseOut];
    SKAction *verticalScaleAction = [SKAction actionWithEffect:verticalScaleEffect];
    
    [cookieBundleSprite runAction:verticalScaleAction];
    
}


- (void)playCookiesFireworksAnimationAtPosition:(CGPoint)position
{
    
    SKEmitterNode *previousFireworksParticle = (SKEmitterNode *)[_hudLayer childNodeWithName:@"fireworkParticle"];
    
    if ( previousFireworksParticle != nil ) {
        [previousFireworksParticle removeFromParent];
        previousFireworksParticle = nil;
    }
    
    NSString *myParticlePath = [[NSBundle mainBundle] pathForResource:@"FireworksParticle" ofType:@"sks"];
    
    for (int i = 0; i < 6; i++) {
        
        SKTexture *cookieTexture = _cookiesTextures[i];
        
        SKEmitterNode *fireworkParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
        fireworkParticle.name = @"fireworkParticle";
        [fireworkParticle setParticleTexture:cookieTexture];
        fireworkParticle.particleAlpha = 1;
        fireworkParticle.position = position;
        [_hudLayer addChild:fireworkParticle];
        
    }
    
}


- (void)playStarsFireworksAnimationAtPosition:(CGPoint)position
{
    
    NSString *myParticlePath = [[NSBundle mainBundle] pathForResource:@"FireworksParticle" ofType:@"sks"];
    
    SKTexture *starTexture = [SKTexture textureWithImageNamed:@"coin"]; // star
    
    SKEmitterNode *starsFireworkParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
    starsFireworkParticle.name = @"starsParticle";
    [starsFireworkParticle setParticleTexture:starTexture];
    starsFireworkParticle.position = position;
    starsFireworkParticle.particleBirthRate = 1000;
    starsFireworkParticle.numParticlesToEmit = 100;
    starsFireworkParticle.particleScale = 0.7;
    starsFireworkParticle.particleScaleRange = 0.2;

    // Change color from Yellow to White
//    SKKeyframeSequence *colorSequence = [[SKKeyframeSequence alloc] initWithCapacity:2];
//    [colorSequence addKeyframeValue:[SKColor yellowColor] time:0];
//    [colorSequence addKeyframeValue:[SKColor whiteColor] time:0.5];
//    starsFireworkParticle.particleColorSequence = colorSequence;
//    starsFireworkParticle.particleColorBlendFactor = 1.0;
    starsFireworkParticle.particleAlpha = 1;
    
    [_hudLayer addChild:starsFireworkParticle];
    
}


- (void)playFlashingStarsAnimation
{
    
    for ( int i = 0 ; i < 75; i++) {
        
        SKSpriteNode *starSprite = [SKSpriteNode spriteNodeWithTexture:_starSprite.texture];
        
        int maxX = self.size.width;
        int maxY = self.size.height;
        int randX = 0 + arc4random_uniform(maxX - 0 + 1);
        int randY = 0 + arc4random_uniform(maxY - 0 + 1);
        
        CGPoint position = CGPointMake(randX, randY);
        starSprite.position = position;
        starSprite.alpha = 0;
        [_hudLayer addChild:starSprite];
        
        double randScale = (drand48() * 0.5) + 0.3;
        [starSprite setScale:randScale/2];
        
        double randWait = (drand48() * 0.2) + 0.2;
        SKAction *waitBeforeShow = [SKAction waitForDuration:randWait];
        SKAction *fadeIn = [SKAction fadeAlphaTo:1 duration:randWait];
        SKAction *fadeOut = [SKAction fadeAlphaTo:0 duration:randWait];
        
        SKAction *fadeSequence = [SKAction sequence:@[waitBeforeShow, fadeIn, waitBeforeShow, fadeOut]];
        
        SKAction *rotate = [SKAction rotateByAngle:DEGREES_TO_RADIANS(30) duration:randWait*6];
        
        SKAction *scaleUp = [SKAction scaleTo:randScale duration:randWait * 3];
        SKAction *scaleDown = [SKAction scaleTo:randScale/2 duration:randWait];
        SKAction *scaleSequence = [SKAction sequence:@[scaleUp, scaleDown]];
        
        SKAction *groupActon = [SKAction group:@[fadeSequence, rotate, scaleSequence]];
        
        SKAction *repeatAction = [SKAction repeatAction:groupActon count:50];
        
        [starSprite runAction:repeatAction];
        
    }
    
}


- (void)playWinRepeatActionsAfterDuration:(CGFloat)duration
{
    
    __weak RWTMyScene *weakSelf = self;
    __weak AudioController *weakAudioController = [AudioController sharedInstance];
    
    SKAction *blockAction = [SKAction runBlock:^{
        
        [weakSelf playCookiesFireworksAnimationAtPosition:_localPlayerLayer.heroSprite.position];
        [weakSelf playBounceAnimationOnHero:_localPlayerLayer.heroSprite scaleFactor:1.25];
        [weakSelf playBounceCookiesBundleAnimationWithScaleFactor:1.15];
        [weakAudioController playEffect:kCandiesFireworksSound];
    }];
    
    SKAction *waitForFireworks = [SKAction waitForDuration:3];
    SKAction *repeatForever = [SKAction repeatActionForever:[SKAction sequence:@[blockAction, waitForFireworks]]];
    
    SKAction *waitToStart = [SKAction waitForDuration:duration];
    SKAction *sequence = [SKAction sequence:@[waitToStart, repeatForever]];
    
    [weakSelf runAction:sequence];
    
}


- (void)showButtonsOnScreenAfterDuration:(CGFloat)duration
{
    
    SKSpriteNode *buttonsPad = [SKSpriteNode spriteNodeWithImageNamed:@"buttonsPad3"];
    buttonsPad.name = @"butonsPad";
    buttonsPad.position = CGPointMake(self.size.width/2, -buttonsPad.size.height/2 - 20);
    [_hudLayer addChild:buttonsPad];
    
    SKSpriteNode *statsPad = [SKSpriteNode spriteNodeWithImageNamed:@"gameOverStatsPad"];
    statsPad.name = @"statsPad";
    statsPad.position = CGPointMake(0, 15);
    [buttonsPad addChild:statsPad];
    
    _backToMenuButton.position = CGPointMake(-115, -10);
    _backToMenuButton.alpha = 0;
    [_backToMenuButton setScale:0];
    [buttonsPad addChild:_backToMenuButton];
    
    _playNewGameButton.position = CGPointMake(115, -10);
    _playNewGameButton.alpha = 0;
    [_playNewGameButton setScale:0];
    [buttonsPad addChild:_playNewGameButton];
    
    SKLabelNode *ratingLabel = [SKLabelNode labelNodeWithFontNamed:@"GillSans-BoldItalic"];
    ratingLabel.fontSize = 20;
    ratingLabel.text = [NSString stringWithFormat:@"%zd",_gameInfo.localPlayer.rank];
    ratingLabel.position = CGPointMake(13, 23);
    [buttonsPad addChild:ratingLabel];
    
    NSInteger ratingPrize;
    NSInteger moneyPrize;
    
    NSInteger localPlayerMoney = [[NSUserDefaults standardUserDefaults] integerForKey:USER_MONEY_KEY];
    
    // Determine who is the winner
    if ( _gameInfo.winnerID == _gameInfo.localPlayer.playerId ) {
        
        ratingPrize = 10;
        moneyPrize = 30;
        
    } else {
        
        ratingPrize = -5;
        moneyPrize = 10;
        
    }
    
    [[NSUserDefaults standardUserDefaults] setInteger:localPlayerMoney + moneyPrize forKey:USER_MONEY_KEY];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    SKLabelNode *ratingPrizeLabel = [SKLabelNode labelNodeWithFontNamed:@"GillSans-BoldItalic"];
    ratingPrizeLabel.fontSize = 15;
    ratingPrizeLabel.text = @"0";
    ratingPrizeLabel.position = CGPointMake(ratingLabel.position.x + 5, ratingLabel.position.y + 15);
    [buttonsPad addChild:ratingPrizeLabel];
    
    SKLabelNode *moneyLabel = [SKLabelNode labelNodeWithFontNamed:@"GillSans-BoldItalic"];
    moneyLabel.fontSize = 20;
    moneyLabel.text = [NSString stringWithFormat:@"%zd",localPlayerMoney];
    moneyLabel.position = CGPointMake(13, -20);
    [buttonsPad addChild:moneyLabel];
    
    SKLabelNode *moneyPrizeLabel = [SKLabelNode labelNodeWithFontNamed:@"GillSans-BoldItalic"];
    moneyPrizeLabel.fontSize = 15;
    moneyPrizeLabel.text = @"0";
    moneyPrizeLabel.position = CGPointMake(moneyLabel.position.x + 5, moneyLabel.position.y + 15);
    [buttonsPad addChild:moneyPrizeLabel];
    
    SKAction *waitToShowButtons = [SKAction waitForDuration:duration + 0.5];
    SKAction *scaleToMax = [SKAction scaleTo:1 duration:0.5];
    scaleToMax.timingMode = SKActionTimingEaseOut;
    SKAction *fadeIn = [SKAction fadeAlphaTo:1 duration:0.5];
    fadeIn.timingMode = SKActionTimingEaseOut;
    
    SKAction *wobbleAction = [self createWobbleAnimation];
    
    SKAction *backButtonSequence = [SKAction sequence:@[waitToShowButtons, [SKAction group:@[scaleToMax, fadeIn]], wobbleAction]];
    
    double randWait = drand48() * 1.0;
    SKAction *wait = [SKAction waitForDuration:randWait];
    SKAction *secondWobbleAction = [SKAction sequence:@[wait, wobbleAction]];
    SKAction *playButtonSequence = [SKAction sequence:@[waitToShowButtons, [SKAction group:@[scaleToMax, fadeIn]], secondWobbleAction]];
    
    [_backToMenuButton runAction:backButtonSequence];
    [_playNewGameButton runAction:playButtonSequence];
    
    SKAction *waitToShowPad = [SKAction waitForDuration:duration];
    SKAction *moveUp = [SKAction moveByX:0 y:buttonsPad.size.height + 20 duration:0.5];
    moveUp.timingMode = SKActionTimingEaseOut;
    
    [buttonsPad runAction:[SKAction sequence:@[waitToShowPad, moveUp]]];
    
    CGFloat waitBeforeCounting = duration + 0.5f;
    
    [self animateCountingLabel:ratingLabel fromValue:_gameInfo.localPlayer.rank withAmount:ratingPrize afterDuration:waitBeforeCounting pricePrefix:@""];
    [self animateCountingLabel:moneyLabel fromValue:localPlayerMoney withAmount:moneyPrize afterDuration:waitBeforeCounting pricePrefix:@""];
    [self animateCountingLabel:moneyPrizeLabel fromValue:0 withAmount:moneyPrize afterDuration:waitBeforeCounting pricePrefix:@"+"];
    [self animateCountingLabel:ratingPrizeLabel fromValue:0 withAmount:ratingPrize afterDuration:waitBeforeCounting pricePrefix:ratingPrize > 0 ? @"+" : @""];
    
    [self playCountSoundAfterDuration:waitBeforeCounting];
    
}


- (void)animateCountingLabel:(SKLabelNode *)label fromValue:(NSInteger)initialValue withAmount:(NSInteger)amount afterDuration:(CGFloat)waitDuration pricePrefix:(NSString *)pricePrefix
{
    
    SKAction *initialWait = [SKAction waitForDuration:waitDuration];
    SKAction *wait = [SKAction waitForDuration: 0.08f];
    
    __block NSInteger currentValue = initialValue;
    
    SKAction *updateLabel = [SKAction runBlock:^{
        
        currentValue += amount / ABS(amount);
        [label setText:[NSString stringWithFormat:@"%@%zd", pricePrefix, currentValue]];
        
    }];
    
    SKAction *sequence = [SKAction sequence:@[wait, updateLabel]];
    SKAction *repeatAction = [SKAction repeatAction:sequence count:ABS(amount)];
    
    [label runAction:[SKAction sequence:@[initialWait, repeatAction]]];
    
}


- (SKAction *)createWobbleAnimation
{
    
    SKAction *verticalSquash = [SKAction scaleXTo:1.05 y:0.95 duration:0.6];
    SKAction *horizontalSquash = [SKAction scaleXTo:0.95 y:1.05 duration:0.6];
    SKAction *normalize = [SKAction scaleXTo:1 y:1 duration:0.6];
    
    SKAction *wobbleSequance = [SKAction sequence:@[verticalSquash, normalize, horizontalSquash, normalize]];
    SKAction *repeatForever = [SKAction repeatActionForever:wobbleSequance];
    
    return repeatForever;
    
}


- (void)playCountSoundAfterDuration:(CGFloat)waitDuration
{
    
    __weak AudioController *weakAudioController = [AudioController sharedInstance];
    
    NSString *effectName = _gameInfo.winnerID == _gameInfo.localPlayer.playerId ? kCountPointsLongSound : kCountPointsShortSound;
    
    SKAction *waitBeforeCountAction = [SKAction waitForDuration:waitDuration];
    SKAction *blockAction = [SKAction runBlock:^{
        [weakAudioController playEffect:effectName];
    }];
    SKAction *soundAction = [SKAction sequence:@[waitBeforeCountAction, blockAction]];
    
    [self runAction:soundAction];
    
}


- (void)playLoseAnimation
{
    
    __weak RWTMyScene *weakSelf = self;
    
    CGFloat moveDuration = 0.75f;
    [[AudioController sharedInstance] playEffect:kLoseSound];
    
    [weakSelf showWinBackgroundForDuration:moveDuration];
    [weakSelf showYouLoseSpritesAfterDuration:moveDuration];
    [weakSelf moveHero:_otherPlayerLayer.heroSprite toCenterOfScreenForDuration:moveDuration];
    [weakSelf playSpotlightAnimationForSprite:_otherPlayerLayer.heroSprite withScale:2.25 isForever:YES];
    [weakSelf showButtonsOnScreenAfterDuration:2];
    
}


- (void)showYouLoseSpritesAfterDuration:(CGFloat)duration
{
    
    NSInteger youLoseSpritesYPosition = 500;
    
    if ( _isSmallDisplay == YES ) {
        youLoseSpritesYPosition -= 60;
    }
    
    // Create two sprites
    SKSpriteNode *youSprite = [SKSpriteNode spriteNodeWithImageNamed:@"youLoseImage"];
    youSprite.name = @"youLoseImage";
    youSprite.anchorPoint = CGPointMake(1, 0.5);
    youSprite.position = CGPointMake(-youSprite.size.width - 17, youLoseSpritesYPosition);
    [_hudLayer addChild:youSprite];
    
    SKSpriteNode *loseSprite = [SKSpriteNode spriteNodeWithImageNamed:@"loseImage"];
    loseSprite.name = @"loseImage";
    loseSprite.anchorPoint = CGPointMake(0, 0.5);
    loseSprite.position = CGPointMake(self.size.width + loseSprite.size.width, youLoseSpritesYPosition);
    [_hudLayer addChild:loseSprite];
    
    NSTimeInterval moveDuration = 0.25;
    
    CGPoint targetPosition = CGPointMake(self.size.width/2 - 17, youLoseSpritesYPosition);
    
    // Play youSprite animation
    SKAction *youSpriteAnimation = [self createWinSquashAnimationForSprite:youSprite toPosition:targetPosition waitDuration:duration moveDuration:moveDuration];
    [youSprite runAction:youSpriteAnimation];
    
    // Play loseSprite animation
    SKAction *loseSpriteAnimation = [self createWinSquashAnimationForSprite:loseSprite toPosition:targetPosition waitDuration:duration moveDuration:moveDuration];
    [loseSprite runAction:loseSpriteAnimation];
    
    SKAction *waitForExplosion = [SKAction waitForDuration:duration + moveDuration];
    
    __weak RWTMyScene *weakSelf = self;
    
    [loseSprite runAction:waitForExplosion completion:^{
        
        // Add Explosion Particle
        NSString *particlePath = [[NSBundle mainBundle] pathForResource:@"ExplosionParticle" ofType:@"sks"];
        SKEmitterNode *explosionParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:particlePath];
        
        [loseSprite addChild:explosionParticle];
        [weakSelf runAction:BombSound];
        
    }];
    
}


- (void)animateBeginGame
{
    
    [self playWinConditionsAnimation];
    
    self.gameLayer.hidden = NO;
    
    self.gameLayer.position = CGPointMake(0, self.size.height);
    SKAction *action = [SKAction moveBy:CGVectorMake(0, -self.size.height) duration:1];
    action.timingMode = SKActionTimingEaseOut;
    [self.gameLayer runAction:action];
    
    NSInteger localPlayerHeroType = _gameInfo.localPlayer.hero.type;
    NSInteger otherPlayerHeroType = _gameInfo.otherPlayer.hero.type;
    
    // Configure localPlayerLayer
    [_localPlayerLayer setHeroImageWithType:localPlayerHeroType forLocalPlayer:YES];
    [_localPlayerLayer setPlayerInfoForPlayer:_gameInfo.localPlayer];
    
    // Configure otherPlayerLayer
    [_otherPlayerLayer setHeroImageWithType:otherPlayerHeroType forLocalPlayer:NO];
    [_otherPlayerLayer setPlayerInfoForPlayer:_gameInfo.otherPlayer];
    
}


- (void)removeAllCookieSprites
{
    
    // Moves all Cookies from their Positions to Center of the Board
    SKAction *scaleDown = [SKAction scaleTo:0.8 duration:0.3];
    
    CGPoint centerPosition = CGPointMake(self.size.width / 2 - TileWidth/2 , TileWidth*NumColumns/2 + TileHeight / 2 );
    SKAction *moveToCenter = [SKAction moveTo:centerPosition duration:0.4];
    
    SKAction *removeFromParent = [SKAction removeFromParent];

    for (SKSpriteNode *cookieSprite in self.cookiesLayer.children) {
        
        double randWait = drand48() * 0.3;
        SKAction *waitBeforeMove = [SKAction waitForDuration:randWait];
        
        SKAction *moveToCenterSequence = [SKAction sequence:@[waitBeforeMove, moveToCenter]];
        SKAction *groupAction = [SKAction group:@[scaleDown, moveToCenterSequence]];
        SKAction *removeCookieAction = [SKAction sequence:@[groupAction, removeFromParent]];
        
        [cookieSprite runAction:removeCookieAction];
    }
    
}


#pragma mark - Server Calls
- (void)performSwapFromServer:(NSNotification *)notification
{
    
    NSDictionary *userInfo = notification.userInfo;
    RWTSwap *swap = userInfo[@"swap"];
        
    self.swipeHandler(swap, YES);
    
}


- (void)resetTimerAndShouldShowHint:(BOOL)shouldShowHint additionalDelay:(float)delay
{

    NSLog(@"Reset Timer! ShouldShowHint: %@", shouldShowHint ? @"YES" : @"NO");
    
    [self removeActionForKey:@"countdown"];
    
    _shouldShowHint = shouldShowHint;
    _moveLength = MAX_MOVE_LENGTH + delay;
    
    [self startTimer];
    
    [self performSelector:@selector(changeTurnIndicator) withObject:nil afterDelay:delay];
    
//    [self changeTurnIndicator];
    
}


-(void)changeTurnIndicator
{
    
    if ( _shouldShowHint == YES ) {
        
        [_timerPad setTexture:[SKTexture textureWithImageNamed:@"timerPadGreen"]];
        [_countDownLabel setFontColor:[UIColor colorWithRed:77.0/255.0 green:250.0/255.0 blue:12.0/255.0 alpha:1]];
        [_localPlayerLayer.turnIndicatorSprite runAction:[SKAction fadeAlphaTo:1 duration:0.2]];
        [_otherPlayerLayer.turnIndicatorSprite runAction:[SKAction fadeAlphaTo:0 duration:0.2]];
        
    } else {
        
        [_timerPad setTexture:[SKTexture textureWithImageNamed:@"timerPadOrange"]];
        [_countDownLabel setFontColor:[UIColor colorWithRed:255.0/255.0 green:160.0/255.0 blue:12.0/255.0 alpha:1]];
        [_localPlayerLayer.turnIndicatorSprite runAction:[SKAction fadeAlphaTo:0 duration:0.2]];
        [_otherPlayerLayer.turnIndicatorSprite runAction:[SKAction fadeAlphaTo:1 duration:0.2]];
        
    }
    
}


-(void)startTimer
{
    
    SKAction *wait = [SKAction waitForDuration:1.0f];
    SKAction *performSelector = [SKAction performSelector:@selector(countDown) onTarget:self];
    SKAction *sequence = [SKAction sequence:@[performSelector, wait]];
    SKAction *repeat   = [SKAction repeatActionForever:sequence];
    
    [self runAction:repeat withKey:@"countdown"];
    
}


-(void)countDown
{
    
    if ( _moveLength > 0 ) {
        
        _moveLength--;

        if ( _moveLength <= 20 ) {
            [_countDownLabel setText:[NSString stringWithFormat:@"%zd", _moveLength]];
        }
        
        if ( (_moveLength == 5 || _moveLength == 10) && _shouldShowHint ) {
            
            [self showHint];
            
        }
        
        if ( _moveLength <= 5 ) {
            
            [[AudioController sharedInstance] playEffect:kClockTickSound];
            
        }
        
        // Stop the countdown if Game is Over
        if ( _gameInfo.isGameOver == YES ) {
            [self removeActionForKey:@"countdown"];
        }
        
    } else {
        [self removeActionForKey:@"countdown"];
    }
    
}


- (void)showHint
{
    
    RWTSwap *hintSwap = [_level getHintPossibleSwap];
    
    SKAction *scaleUp = [SKAction scaleTo:1.4 duration:0.2];
    SKAction *scaleDown = [SKAction scaleTo:0.9 duration:0.2];
    SKAction *scaleToNormal = [SKAction scaleTo:1 duration:0.1];
    
    SKAction *sequenceAction = [SKAction sequence:@[scaleUp, scaleDown, scaleUp, scaleDown, scaleToNormal]];
    
    [hintSwap.cookieA.sprite runAction:sequenceAction];
    [hintSwap.cookieB.sprite runAction:sequenceAction];
    
}


#pragma mark - Update Labels
- (void)updateLabels
{
    
    NSDictionary *gameWinConditions = _gameInfo.gameWinConditions;
    
    // Update Local Player labels
    Player *localPlayer = _gameInfo.localPlayer;
    _localPlayerLayer.nameLabel.text = [NSString stringWithFormat:@"%@", localPlayer.name];
    
    NSInteger cookieType1Counter = MAX(0, [gameWinConditions[@"1"] integerValue] - localPlayer.cookieType1Counter);
    _localPlayerLayer.cookieType1CounterLabel.text = [NSString stringWithFormat:@"%zd", cookieType1Counter];
    
    NSInteger cookieType2Counter = MAX(0, [gameWinConditions[@"2"] integerValue] - localPlayer.cookieType2Counter);
    _localPlayerLayer.cookieType2CounterLabel.text = [NSString stringWithFormat:@"%zd", cookieType2Counter];
    
    NSInteger cookieType3Counter = MAX(0, [gameWinConditions[@"3"] integerValue] - localPlayer.cookieType3Counter);
    _localPlayerLayer.cookieType3CounterLabel.text = [NSString stringWithFormat:@"%zd", cookieType3Counter];
    
    NSInteger cookieType4Counter = MAX(0, [gameWinConditions[@"4"] integerValue] - localPlayer.cookieType4Counter);
    _localPlayerLayer.cookieType4CounterLabel.text = [NSString stringWithFormat:@"%zd", cookieType4Counter];
    
    _localPlayerLayer.cookieType5CounterLabel.text = [NSString stringWithFormat:@"%zd/%@", localPlayer.hero.cookieType5Counter, localPlayer.hero.usageConditions[@"5"]];
    _localPlayerLayer.cookieType6CounterLabel.text = [NSString stringWithFormat:@"%zd/%@", localPlayer.hero.cookieType6Counter, localPlayer.hero.usageConditions[@"6"]];
    
    // Update Other Player labels
    Player *otherPlayer = _gameInfo.otherPlayer;
    _otherPlayerLayer.nameLabel.text = [NSString stringWithFormat:@"%@", otherPlayer.name];
    
    cookieType1Counter = MAX(0, [gameWinConditions[@"1"] integerValue] - otherPlayer.cookieType1Counter);
    _otherPlayerLayer.cookieType1CounterLabel.text = [NSString stringWithFormat:@"%zd", cookieType1Counter];
    
    cookieType2Counter = MAX(0, [gameWinConditions[@"2"] integerValue] - otherPlayer.cookieType2Counter);
    _otherPlayerLayer.cookieType2CounterLabel.text = [NSString stringWithFormat:@"%zd", cookieType2Counter];
    
    cookieType3Counter = MAX(0, [gameWinConditions[@"3"] integerValue] - otherPlayer.cookieType3Counter);
    _otherPlayerLayer.cookieType3CounterLabel.text = [NSString stringWithFormat:@"%zd", cookieType3Counter];
    
    cookieType4Counter = MAX(0, [gameWinConditions[@"4"] integerValue] - otherPlayer.cookieType4Counter);
    _otherPlayerLayer.cookieType4CounterLabel.text = [NSString stringWithFormat:@"%zd", cookieType4Counter];
    
    _otherPlayerLayer.cookieType5CounterLabel.text = [NSString stringWithFormat:@"%zd/%@", otherPlayer.hero.cookieType5Counter, otherPlayer.hero.usageConditions[@"5"]];
    _otherPlayerLayer.cookieType6CounterLabel.text = [NSString stringWithFormat:@"%zd/%@", otherPlayer.hero.cookieType6Counter, otherPlayer.hero.usageConditions[@"6"]];
    
}


- (SKAction *)getUpdateLabelsAction
{
    
    __weak RWTMyScene *weakSelf = self;
    SKAction *updateLabelsAction = [SKAction runBlock:^{
        [weakSelf updateLabels];
    }];
    
    return updateLabelsAction;
    
}


#pragma mark - Animations

- (void)playCurveAnimationForCookie:(SKSpriteNode *)cookieSprite toTargetCookie:(SKSpriteNode *)targetCookie minAngle:(int)minAngle maxAngle:(int)maxAngle speed:(NSInteger)speed radius:(NSInteger)radius isHeroSkill:(BOOL)isHeroSkill particleColor:(UIColor *)particleColor
{
    
    if ( [cookieSprite actionForKey:@"squash"] != nil ) {
        
        // stop Squash animations
        [cookieSprite removeActionForKey:@"squash"];
        [cookieSprite setScale:1];
        
    }
    
    // set anchor point back to center and change the position to be the same as before
    cookieSprite.anchorPoint = CGPointMake(0.5, 0.5);
    cookieSprite.position = CGPointAdd(cookieSprite.position, CGPointMake(0, cookieSprite.size.height/2));
    cookieSprite.zPosition = 100;
    
    int randNum = arc4random_uniform(maxAngle - minAngle) + minAngle;
    CGFloat angle = DEGREES_TO_RADIANS(randNum);
    
    CGFloat x = 0 + radius * cos(angle);
    CGFloat y = 0 + radius * sin(angle);
    
    CGPoint controlPoint = CGPointMake(x, y);

    CGPoint realCookiePosition = CGPointSubtract(CGPointZero, cookieSprite.position);
    CGPoint targetPoint = CGPointAdd(realCookiePosition, targetCookie.position);
    
    UIBezierPath *path = [[UIBezierPath alloc] init];
    [path moveToPoint:CGPointZero];
    [path addQuadCurveToPoint:targetPoint controlPoint:controlPoint];
    
    // Move cookieSprite
    SKAction *move;
    
    if ( SYSTEM_VERSION_LESS_THAN(@"8") == YES ) {
        move = [SKAction followPath:path.CGPath duration:0.8];
    } else {
        move = [SKAction followPath:path.CGPath speed:speed];
    }
    
    SKAction *removeFromParent = [SKAction removeFromParent];
    SKAction *moveCookieSequence = [SKAction sequence:@[move, removeFromParent]];
    
    SKAction *groupAction;
    
    if ( isHeroSkill == NO ) {
        
        // Add Particle
        NSString *particlePath = [[NSBundle mainBundle] pathForResource:@"FollowFingerMagicParticle" ofType:@"sks"];
        SKEmitterNode *followParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:particlePath];
        followParticle.particleColorSequence = nil;
        followParticle.particleColorBlendFactor = 1.0;
        followParticle.particleColor = particleColor;
        followParticle.particleScale = 0.15;
        followParticle.targetNode = cookieSprite.parent;
        followParticle.zPosition = 100;
        [cookieSprite addChild:followParticle];
        
        // Bounce Cookie
        SKAction *scaleUpCookie = [SKAction scaleTo:1.4 duration:0.2];
        SKAction *scaleDownCookie = [SKAction scaleTo:0.8 duration:0.3];
        SKAction *scaleCookieSequence = [SKAction sequence:@[scaleUpCookie,scaleDownCookie]];
        
        groupAction = [SKAction group:@[moveCookieSequence, scaleCookieSequence]];
        
    } else {
        
        // Bounce Cookie
        SKAction *scaleUpCookie = [SKAction scaleTo:1.0 duration:0.4];
        SKAction *scaleDownCookie = [SKAction scaleTo:0.8 duration:0.2];
        SKAction *scaleCookieSequence = [SKAction sequence:@[scaleUpCookie,scaleDownCookie]];
        
        groupAction = [SKAction group:@[moveCookieSequence, scaleCookieSequence]];
        
    }
    
    // Bounce targetCookie
    SKAction *scaleUpTarget = [SKAction scaleTo:1.5 duration:0.15];
    [scaleUpTarget setTimingMode:SKActionTimingEaseOut];
    SKAction *scaleToNormal = [SKAction scaleTo:1 duration:0.1];
    SKAction *bounce = [SKAction sequence:@[scaleUpTarget, scaleToNormal]];
    
    __weak RWTMyScene *weakSelf = self;
    
    // do NOT update labels when should use hero
    BOOL shouldUpdateLabels = _gameInfo.shouldUseHero == YES ? NO : YES;
    
    [cookieSprite runAction:groupAction completion:^{
        
        if ( shouldUpdateLabels == YES ) {
            [weakSelf runAction:[weakSelf getUpdateLabelsAction]];
        }
        
        [targetCookie runAction:bounce];
        
    }];
    
}


- (SKColor *)getColorForCookieType:(NSInteger)cookieType
{
    
    switch ( cookieType ) {
        case 1:
            return [SKColor orangeColor];
            
        case 2:
            // green
            return [SKColor greenColor];
            
        case 3:
            // pink
            return [SKColor colorWithRed:255/255.0f green:110/255.0f blue:255/255.0f alpha:1];

        case 4:
            return [SKColor blueColor];

        case 5:
            return [SKColor yellowColor];

        case 6:
            return [SKColor redColor];

        default:
            return nil;
    }
    
}


/*
-(void)playRayAnimationFromPoint:(CGPoint)startPoint toSprite:(SKSpriteNode *)targetSprite completion:(void (^)())block
{
    
    CGPoint difference = CGPointSubtract(targetSprite.position, startPoint);
    CGFloat rotationRadians = CGPointToAngle(difference);
    rotationRadians -= 1.5707963268; // 90 degrees
    
    SKSpriteNode *raySprite = [SKSpriteNode spriteNodeWithTexture:_rayAnimationTextures[0]];
    raySprite.position = startPoint;
    raySprite.zRotation = rotationRadians;
    raySprite.anchorPoint = CGPointMake(0.5, 0);
    raySprite.zPosition = 1000;
    [targetSprite.parent addChild:raySprite];
    
    CGFloat dist = CGPointDistance(targetSprite.position, startPoint);
    CGFloat scale = dist / raySprite.size.width;
    
    SKAction *animateAction = [SKAction animateWithTextures:_rayAnimationTextures timePerFrame:0.04];
    SKAction *scaleAction = [SKAction scaleXBy:1 y:scale duration:0];
    SKAction *removeAction = [SKAction removeFromParent];
    
    SKAction *sequence = [SKAction sequence:@[scaleAction, animateAction, removeAction]];
    
    [raySprite runAction:sequence completion:^{
        block ? block() : nil;
    }];
    
}
*/

- (SKSpriteNode *)getTargetCookieForCookieType:(NSUInteger)cookieType inPlayerLayer:(PlayerInterfaceSprite *)playerLayer
{
    
    switch ( cookieType ) {
        case 1:
            return playerLayer.cookieType1Sprite;
            
        case 2:
            return playerLayer.cookieType2Sprite;
            
        case 3:
            return playerLayer.cookieType3Sprite;
            
        case 4:
            return playerLayer.cookieType4Sprite;
            
        case 5:
            return playerLayer.cookieType5Sprite;
            
        case 6:
            return playerLayer.cookieType6Sprite;
            
        default:
            NSLog(@"THERE IS NO SUCH COOKIE TYPE !!!");
            return  nil;
    }
    
}


- (SKSpriteNode *)getTargetCookieForCookie:(RWTCookie *)cookie
{
    
    // Local Player Move
    if ( _level.isSimulation == NO ) {
        
        // move cookie from _cookiesLayer to _localPlayerLayer
        CGPoint scenePoint = [self convertPoint:cookie.sprite.position fromNode:_cookiesLayer];
        CGPoint localPoint = [self convertPoint:scenePoint toNode:_localPlayerLayer];
        [cookie.sprite removeFromParent];
        [_localPlayerLayer addChild:cookie.sprite];
        cookie.sprite.position = localPoint;

        return [self getTargetCookieForCookieType:cookie.cookieType inPlayerLayer:_localPlayerLayer];
        
    } else {
        // Other Player Move
        
        // move cookie from _cookiesLayer to _otherPlayerLayer with acurate coordinates
        CGPoint scenePoint = [self convertPoint:cookie.sprite.position fromNode:_cookiesLayer];
        CGPoint localPoint = [self convertPoint:scenePoint toNode:_otherPlayerLayer];
        [cookie.sprite removeFromParent];
        [_otherPlayerLayer addChild:cookie.sprite];
        cookie.sprite.position = localPoint;

        return [self getTargetCookieForCookieType:cookie.cookieType inPlayerLayer:_otherPlayerLayer];
        
    }
    
}


- (void)playSpotlightAnimationForSprite:(SKSpriteNode *)sprite withScale:(CGFloat)scaleFactor isForever:(BOOL)isForever
{
    
    // Create two sprites
    SKSpriteNode *firstSpotlight = [SKSpriteNode spriteNodeWithImageNamed:@"spotlight_2"];
    firstSpotlight.alpha = 0;
    [firstSpotlight setScale:scaleFactor];
    firstSpotlight.zPosition = -1;
    
    SKSpriteNode *secondSpotlight = [SKSpriteNode spriteNodeWithImageNamed:@"spotlight_2"];
    secondSpotlight.alpha = 0;
    [secondSpotlight setScale:scaleFactor];
    secondSpotlight.zPosition = -1;
    
    // Create actions for sprites
    SKAction *removeFromParent = [SKAction removeFromParent];
    SKAction *fadeOut = [SKAction fadeAlphaTo:0 duration:0.1];
    SKAction *fadeIn = [SKAction fadeAlphaTo:1 duration:0.1];
    
    CGFloat rotationDuration = 1;
    SKAction *wait = [SKAction waitForDuration:rotationDuration];
    SKAction *fadeSequence = [SKAction sequence:@[fadeIn, wait, fadeOut]];
    
    // Rotate one to the Left and one to the Right
    SKAction *rotateClockwise = [SKAction rotateByAngle:M_PI * 0.35 duration:rotationDuration];
    SKAction *rotateClockwiseSequence = [SKAction sequence:@[rotateClockwise, removeFromParent]];
    
    SKAction *rotateCounterClockwise = [SKAction rotateByAngle:-M_PI * 0.35 duration:rotationDuration];
    SKAction *rotateCounterClockwiseSequence = [SKAction sequence:@[rotateCounterClockwise, removeFromParent]];
    
    if ( isForever == YES ) {
        
        // Show sprites when rotation begins
        [firstSpotlight runAction:fadeIn];
        [secondSpotlight runAction:fadeIn];
        
        // Rotate forever
        [firstSpotlight runAction:[SKAction repeatActionForever:rotateClockwise]];
        [secondSpotlight runAction:[SKAction repeatActionForever:rotateCounterClockwise]];
        
    } else {
        
        // Hide sprites when rotation ends
        [firstSpotlight runAction:fadeSequence];
        [secondSpotlight runAction:fadeSequence];
        
        // Rotate only once
        [firstSpotlight runAction:rotateClockwiseSequence];
        [secondSpotlight runAction:rotateCounterClockwiseSequence];
        
    }
    
    [sprite addChild:firstSpotlight];
    [sprite addChild:secondSpotlight];
    sprite.zPosition = 1;
    
}


- (void)playShuffleImageAnimation
{
    
    [self showInfoBannerWithImageName:@"shuffleImage4" withDuration:1.5f isForCombo:NO];
    [[AudioController sharedInstance] playEffect:kShowBannerSound];
    
}


- (void)showInfoBannerWithImageName:(NSString *)imageName withDuration:(CGFloat)duration isForCombo:(BOOL)isForCombo
{
    
    SKSpriteNode *bannerSprite = [SKSpriteNode spriteNodeWithImageNamed:imageName];
    bannerSprite.position = CGPointMake(self.size.width/2, - bannerSprite.size.height/2);
    [_hudLayer addChild:bannerSprite];
    
    CGPoint targetPosition;
    SKAction *playSoundEffect;
    
    if ( isForCombo == YES ) {
        
        targetPosition = CGPointMake(self.size.width/2, bannerSprite.size.height / 2);
        
        // create empty SKAction
        playSoundEffect = [SKAction waitForDuration:0];
        
        [[AudioController sharedInstance] playEffect:kMatch4or5CookiesSound];
        
    } else {
        
        // Disable touches when Shuffle animation start
        _canMakeMove = NO;
        
        targetPosition = CGPointMake(self.size.width/2, TileWidth*NumColumns/2 + TileHeight);
        
        __weak AudioController *weakAudioController = [AudioController sharedInstance];
        playSoundEffect = [SKAction runBlock:^{
            [weakAudioController playEffect:kShowBannerSound];
        }];
        
    }
    
    SKAction *moveToTargetPosition = [SKAction moveTo:targetPosition duration:0.25];
    moveToTargetPosition.timingMode = SKActionTimingEaseOut;
    
    SKAction *wait = [SKAction waitForDuration:duration];
    
    SKAction *moveOutOfScreen = [SKAction moveBy:CGVectorMake(0, -self.size.height/2) duration:0.25];
    moveOutOfScreen.timingMode = SKActionTimingEaseIn;
    
    __weak RWTMyScene *weakSelf = self;
    
    [bannerSprite runAction:[SKAction sequence:@[moveToTargetPosition, wait, playSoundEffect, moveOutOfScreen]] completion:^{
        
        if ( isForCombo == NO ) {
            
            // Enable touches when Shuffle animation ends
            weakSelf.canMakeMove = YES;
            
        }
        
    }];
    
}


- (void)playHeroAnimationIfNecessary
{
    
    if ( _gameInfo.shouldUseHero == YES ) {
        
        if ( _level.isSimulation == NO ) {
            
            NSLog(@"use LOCAL player HERO");
            _gameInfo.shouldUseHero = NO;
            
            [_gameInfo.localPlayer performHeroSkillWithOtherPlayer:_gameInfo.otherPlayer];
            [self playSpotlightAnimationForSprite:_localPlayerLayer.heroSprite withScale:2 isForever:NO];
            [self playAnimationHeroSkillsOnMyself:_gameInfo.localPlayer.hero.skillsOnMyself inPlayerLayer:_localPlayerLayer];
            [self playBounceAnimationOnHero:_localPlayerLayer.heroFrameSprite scaleFactor:1.5];
            
            BOOL shouldThrowBomb = [self shouldThrowBombForHeroSkills:_gameInfo.localPlayer.hero.skillsOnOtherPlayer];
            
            if ( shouldThrowBomb == YES ) {
                [self throwBombAnimationFromHero:_localPlayerLayer.heroFrameSprite toPlayerLayer:_otherPlayerLayer];
            }
            
            // Plays RayAnimation
//            [self playAnimationHeroSkillsOnOtherPlayer:_gameInfo.localPlayer.hero.skillsOnOtherPlayer fromHero:_localPlayerLayer.heroFrameSprite toPlayerLayer:_otherPlayerLayer];
            
        } else {
            
            NSLog(@"use OTHER player HERO");
            _gameInfo.shouldUseHero = NO;
            
            [_gameInfo.otherPlayer performHeroSkillWithOtherPlayer:_gameInfo.localPlayer];
            [self playSpotlightAnimationForSprite:_otherPlayerLayer.heroSprite withScale:2 isForever:NO];
            [self playAnimationHeroSkillsOnMyself:_gameInfo.otherPlayer.hero.skillsOnMyself inPlayerLayer:_otherPlayerLayer];
            [self playBounceAnimationOnHero:_otherPlayerLayer.heroFrameSprite scaleFactor:1.5];
            
            BOOL shouldThrowBomb = [self shouldThrowBombForHeroSkills:_gameInfo.otherPlayer.hero.skillsOnOtherPlayer];
            
            if ( shouldThrowBomb == YES ) {
                [self throwBombAnimationFromHero:_otherPlayerLayer.heroFrameSprite toPlayerLayer:_localPlayerLayer];
            }
            
            // Plays RayAnimation
//            [self playAnimationHeroSkillsOnOtherPlayer:_gameInfo.otherPlayer.hero.skillsOnOtherPlayer fromHero:_otherPlayerLayer.heroFrameSprite toPlayerLayer:_localPlayerLayer];
            
        }
        
    }
    
}


- (void)playAnimationHeroSkillsOnMyself:(NSDictionary *)heroSkillsOnMyself inPlayerLayer:(PlayerInterfaceSprite *)playerLayer
{
    
    // at indexes from 1 to 6 are values for the cookies counters
    for (int i = 1; i <= [heroSkillsOnMyself count]; i++) {
        
        NSString *counterKey = [NSString stringWithFormat:@"%d", i];
        NSInteger skillValue = [heroSkillsOnMyself[counterKey] integerValue];
        
        // Do not play animation if there is no Skill for this CookieType
        if ( skillValue == 0 ) {
            break;
        }
        
        // Create cookie to animate
        RWTCookie *cookie = [[RWTCookie alloc] init];
        cookie.cookieType = i;
        
        SKSpriteNode *targetCookie = [self getTargetCookieForCookieType:cookie.cookieType inPlayerLayer:playerLayer];
        
        SKAction *wait = [SKAction waitForDuration:0.15 * i];
        
        __weak RWTMyScene *weakSelf = self;
        
        [self runAction:wait completion:^{
            
            for (int k = 0; k < skillValue; k++) {
                
                SKSpriteNode *cookieSprite = [SKSpriteNode spriteNodeWithImageNamed:cookie.spriteName];
                [cookieSprite setScale:0.7];
                cookieSprite.anchorPoint = CGPointMake(0.5, 0);
                cookieSprite.position = playerLayer.heroFrameSprite.position;
                [playerLayer addChild:cookieSprite];
                
                SKColor *particleColor = [weakSelf getColorForCookieType:cookie.cookieType];
                
                // run animaion
                [weakSelf playCurveAnimationForCookie:cookieSprite toTargetCookie:targetCookie minAngle:210 maxAngle:330 speed:210 radius:170 isHeroSkill:YES particleColor:particleColor];
                
            }
            
        }];
        
    }
    
}


- (BOOL)shouldThrowBombForHeroSkills:(NSDictionary *)heroSkillsOnOtherPlayer
{
    
    BOOL shouldThrowBomb = NO;
    
    // at indexes from 1 to 6 are values for the cookies counters
    for (int i = 1; i <= [heroSkillsOnOtherPlayer count]; i++) {
        
        NSString *counterKey = [NSString stringWithFormat:@"%d", i];
        NSInteger skillValue = [heroSkillsOnOtherPlayer[counterKey] integerValue];
        
        // Do not play animation if there is no Skill for at least one of the CookieTypes
        if ( skillValue != 0 ) {
            
            shouldThrowBomb = YES;
            break;
            
        }
        
    }
    
    return shouldThrowBomb;
    
}


- (void)throwBombAnimationFromHero:(SKSpriteNode *)hero toPlayerLayer:(PlayerInterfaceSprite *)playerLayer
{
    
    [self runAction:ThrowBombSound];
    
    // Convert points to get acurate Hero position inside playerLayer
    CGPoint heroPositionInScene = [self convertPoint:hero.position fromNode:hero.parent];
    CGPoint heroPositionInOtherPlayerLayer = [self convertPoint:heroPositionInScene toNode:playerLayer];
    
    CGPoint centerOfPlayerLayer = CGPointZero;
    
    SKSpriteNode *bombSprite = [SKSpriteNode spriteNodeWithImageNamed:@"bomb"];
    bombSprite.position = heroPositionInOtherPlayerLayer;
    bombSprite.zPosition = 100;
    bombSprite.alpha = 0;
    [bombSprite setScale:0.5];
    [playerLayer addChild:bombSprite];
    
    CGFloat distance = CGPointDistance(bombSprite.position, centerOfPlayerLayer);
    CGPoint controlPoint;
    
    // Determines when the bomb is on the Left or on the Right side of the Target
    if ( bombSprite.position.x > 0 ) {
        controlPoint = CGPointMake(distance / 2, 100);
    } else {
        controlPoint = CGPointMake(-distance / 2, 100);
    }
    
    UIBezierPath *path = [[UIBezierPath alloc] init];
    [path moveToPoint:bombSprite.position];
    [path addQuadCurveToPoint:CGPointZero controlPoint:controlPoint];
    
    CGFloat throwDuration = 0.75;
    
    SKAction *scaleUp = [SKAction scaleTo:1 duration:0.2];
    scaleUp.timingMode = SKActionTimingEaseIn;
    SKAction *fadeIn = [SKAction fadeAlphaTo:1 duration:0.1];
    SKAction *moveToTarget = [SKAction followPath:path.CGPath asOffset:NO orientToPath:NO duration:throwDuration];
    SKAction *rotate = [SKAction rotateByAngle:DEGREES_TO_RADIANS(-360) duration:throwDuration];
    SKAction *group = [SKAction group:@[fadeIn, scaleUp, moveToTarget, rotate]];
    [bombSprite runAction:group];
    
    SKAction *wait = [SKAction waitForDuration:throwDuration];
    SKAction *removeFromParent = [SKAction removeFromParent];
    
    // Create scale animation for opponent playerLayer
    SKTScaleEffect *scaleLayerEffect = [self createHorizontalScaleEffectForSprite:playerLayer scaleFactor:1.15f withDuration:1.75f andTimingFunction:SKTTimingFunctionElasticEaseOut];
    
    __weak RWTMyScene *weakSelf = self;
    
    SKAction *explode = [SKAction runBlock:^{
        
        // Add Particle
        NSString *particlePath = [[NSBundle mainBundle] pathForResource:@"ExplosionParticle" ofType:@"sks"];
        SKEmitterNode *explosionParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:particlePath];
        [playerLayer addChild:explosionParticle];
        
        // Scale opponent playerLayer
        [playerLayer runAction:[SKAction actionWithEffect:scaleLayerEffect]];
        
        [weakSelf runAction:BombSound];
        
    }];
    
    SKAction *sequence = [SKAction sequence:@[wait, explode, removeFromParent]];
    [bombSprite runAction:sequence];
    
}


/* Plays RayAnimation
- (void)playAnimationHeroSkillsOnOtherPlayer:(NSDictionary *)heroSkillsOnOtherPlayer fromHero:(SKSpriteNode *)hero toPlayerLayer:(PlayerInterfaceSprite *)playerLayer
{
    
    BOOL shouldScalePlayerLayer = NO;
    
    // at indexes from 1 to 6 are values for the cookies counters
    for (int i = 1; i <= [heroSkillsOnOtherPlayer count]; i++) {
        
        NSString *counterKey = [NSString stringWithFormat:@"%d", i];
        NSInteger skillValue = [heroSkillsOnOtherPlayer[counterKey] integerValue];
        
        // Do not play animation if there is no Skill for this CookieType
        if ( skillValue == 0 ) {
            break;
        }
        
        SKSpriteNode *targetCookie = [self getTargetCookieForCookieType:i inPlayerLayer:playerLayer];
        
        CGPoint heroPositionInScene = [self convertPoint:hero.position fromNode:hero.parent];
        CGPoint heroPositionInOtherPlayerLayer = [self convertPoint:heroPositionInScene toNode:playerLayer];
        
        // run animaion
        [self playRayAnimationFromPoint:heroPositionInOtherPlayerLayer toSprite:targetCookie completion:nil];
        
        shouldScalePlayerLayer = YES;
        
    }
    
    // Scale playerLayer only if there is RayAnimation
    if ( shouldScalePlayerLayer == YES ) {
        
        SKTScaleEffect *scaleLayerEffect = [self createHorizontalScaleEffectForSprite:playerLayer scaleFactor:1.15f withDuration:1.75f andTimingFunction:SKTTimingFunctionElasticEaseOut];
        [playerLayer runAction:[SKAction actionWithEffect:scaleLayerEffect]];
        
        [self runAction:BlasterSound];
        
    }
    
}
*/

- (void)playBounceAnimationOnHero:(SKSpriteNode *)heroSprite scaleFactor:(CGFloat)scaleFactor
{
    
    SKTScaleEffect *zoomInScaleEffect = [self createZoomScaleEffectForSprite:heroSprite scaleFactor:scaleFactor withDuration:2.25];
    
    [heroSprite runAction:[SKAction actionWithEffect:zoomInScaleEffect]];
    
}


- (void)playComboAnimationIfNecessary
{
    
    if ( _level.isSimulation == NO ) {
        
        if ( _gameInfo.localPlayer.hasMatched4Cookies == YES ) {
            
            _gameInfo.localPlayer.hasMatched4Cookies = NO;
            NSLog(@"play LOCAL animation for 4 matched Cookies");
            [self showInfoBannerWithImageName:@"localPlayerMatched4Cookies" withDuration:1 isForCombo:YES];
            
        }
        
        if ( _gameInfo.localPlayer.hasMatched5Cookies == YES ) {
            
            _gameInfo.localPlayer.hasMatched5Cookies = NO;
            NSLog(@"play LOCAL animation for 5 matched Cookies");
            [self showInfoBannerWithImageName:@"localPlayerMatched5Cookies" withDuration:1 isForCombo:YES];
            
        }
        
    } else {
        
        if ( _gameInfo.otherPlayer.hasMatched4Cookies == YES ) {
            
            _gameInfo.otherPlayer.hasMatched4Cookies = NO;
            NSLog(@"play OTHER animation for 4 matched Cookies");
            [self showInfoBannerWithImageName:@"otherPlayerMatched4Cookies" withDuration:1 isForCombo:YES];
            
        }
        
        if ( _gameInfo.otherPlayer.hasMatched5Cookies == YES ) {
            
            _gameInfo.otherPlayer.hasMatched5Cookies = NO;
            NSLog(@"play OTHER animation for 5 matched Cookies");
            [self showInfoBannerWithImageName:@"otherPlayerMatched5Cookies" withDuration:1 isForCombo:YES];
            
        }
        
    }
    
}


- (void)playWinConditionsAnimation
{
    
    WinConditionsSprite *winConditionsSprite = [[WinConditionsSprite alloc] initWithConditions:_gameInfo.gameWinConditions];
    winConditionsSprite.zPosition = 100;
    winConditionsSprite.position = CGPointMake(self.size.width / 2, 250);
    
    [_hudLayer addChild:winConditionsSprite];
    
    SKAction *moveDown = [SKAction moveByX:0 y:-50 duration:0.25];
    moveDown.timingMode = SKActionTimingEaseOut;
    
    __weak AudioController *weakAudioController = [AudioController sharedInstance];
    SKAction *playSoundEffect = [SKAction runBlock:^{
        [weakAudioController playEffect:kShowBannerSound];
    }];
    
    SKAction *moveUp = [SKAction moveByX:0 y:self.size.height duration:0.8];
    moveUp.timingMode = SKActionTimingEaseIn;
    
    SKAction *wait = [SKAction waitForDuration:4];
    SKAction *fadeOut = [SKAction fadeAlphaTo:0 duration:0.5];
    SKAction *removeFromParent = [SKAction removeFromParent];
    
    SKSpriteNode *glassSprite = (SKSpriteNode *)[_hudLayer childNodeWithName:@"glassSprite"];
    glassSprite.zPosition = winConditionsSprite.zPosition - 1;
    
    __weak RWTMyScene *weakSelf = self;
    
    [winConditionsSprite runAction:[SKAction sequence:@[wait, moveDown, playSoundEffect, moveUp, removeFromParent]] completion:^{
        [glassSprite runAction:[SKAction sequence:@[fadeOut, removeFromParent]]];
        weakSelf.gameState = GameStatePlaying;
    }];
    
}


- (void)cleanScene
{
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [self cleanNode:_menuScreen];
    
    _menuScreen = nil;
    _noButton = nil;
    _yesButton = nil;
    
    // Clean cookie sprites
    for (int col = 0; col < 9; col++) {
        
        for (int row = 0; row < 9; row++) {
            
            RWTCookie *cookie = [_level cookieAtColumn:col row:row];
            
            if ( cookie.sprite != nil ) {
                
                [self cleanNode:cookie.sprite];
                
            }
            
        }
        
    }
    
    [_cookiesLayer removeFromParent];
    _cookiesLayer = nil;
    
    NSArray *children = [_tilesLayer children];
    
    for (SKNode *child in children) {
        [self cleanNode:child];
    }
    
    [_tilesLayer removeFromParent];
    _tilesLayer = nil;
    
    [self cleanNode:_localPlayerLayer.heroSprite];
    
    children = [_hudLayer children];
    
    for (SKNode *child in children) {
        [self cleanNode:child];
    }
    
    [_hudLayer removeFromParent];
    _hudLayer = nil;
    
    children = [_gameLayer children];
    
    for (SKNode *child in children) {
        [self cleanNode:child];
    }
    
    [_gameLayer removeFromParent];
    _gameLayer = nil;
    
    [self cleanNode:_followFingerParticle];
    _followFingerParticle = nil;
    
    [self removeAllActions];
    [self removeAllChildren];
    
    NSLog(@"SCENE CLEANED");
        
}


- (void)cleanNode:(SKNode *)node
{
    
//    [node removeAllActions];
    [node removeFromParent];
    
//    for (SKNode *child in node.children) {
//        [self cleanNode:child];
//    }
//    [node removeAllActions];
//    [node removeFromParent];
    
}


-(void)dealloc
{
    
    NSLog(@"Dealloc: %@", [self class]);
    
}

@end
